#include "sched.h"
#include "tss.h"
#include "gdt.h"
#include "mmu.h"
#include "i386.h"

unsigned short tareas[CANT_TAREAS];
unsigned int *pagina_libre_usuario();
unsigned int* inicializar_dir_usuario();
void mapear_pagina(unsigned int virtual, unsigned int cr3, unsigned int fisica);
gdt_entry *obtener_sig_gdt_libre ();
tss* obtener_entrada_tss();
unsigned short *entradaSched();
unsigned int cantTaskActual;
unsigned int *pagina_libre_kernel();
unsigned int cantTareas();

void inicializar_sched(){
	cantTaskActual = 0;
	int i;
	for ( i=0; i<CANT_TAREAS; i++){
		tareas[i]= 0;
	}
}

unsigned short crear_proceso(unsigned int cargar_desde){
	
	unsigned int cr3Tarea =rcr3();
	lcr3((unsigned int) 0x100000);
	int i;
	unsigned int *codigoTarea = (unsigned int *) cargar_desde;
	
	//Pido página para el código
	unsigned int *pagCodigo = pagina_libre_usuario();
	//Pido página para la pila
	unsigned int *pagPila = pagina_libre_usuario();
	
	//Copio de a 4 bytes (un entero)
	for (i = 0;i < 1024;i++)
		pagCodigo[i] = codigoTarea[i];
		
	//Creo un directorio de tablas de página vacío
	unsigned int * directorio = inicializar_dir_usuario();
	
	//Mapeo las direcciones de las páginas de código y de pila.
	//Como estamos usando identity mapping la dirección lógica y física es la misma
	mapear_pagina((unsigned int)0x0, (unsigned int)directorio, (unsigned int)pagCodigo);
	mapear_pagina((unsigned int)pagPila, (unsigned int)directorio, (unsigned int)pagPila);
		
	//pagPila=(unsigned int*)	0x0010A000;
	//Obtengo una entrada TSS y la completo con la información de la tarea actual
	tss *entradaTSS = obtener_entrada_tss();
	entradaTSS->cr3 = (unsigned int)directorio;
	//entradaTSS->cr3 = (unsigned int)0x00100000;
	entradaTSS->eip = (unsigned int)0x0;
	entradaTSS->eflags = (unsigned int) 0x202;	//interrupciones habilitadas
	entradaTSS->esp = (unsigned int)pagPila + 0xfff - 0x4;	
	entradaTSS->ebp = entradaTSS->esp;
	
	entradaTSS->ss0 = (unsigned short) 0x18; //Segmento de datos - Protegemos con paginacion
	entradaTSS->esp0 = (unsigned int)pagina_libre_kernel() + 0xfff;
	
	entradaTSS->es = (unsigned short) 0x30; //Segmento de datos - Protegemos con paginacion
	entradaTSS->fs = (unsigned short) 0;
	entradaTSS->gs = (unsigned short) 0;
	entradaTSS->cs = (unsigned short) 0x28; //Segmento de código de usuario- Protegemos con paginacion
	entradaTSS->ss = (unsigned short) 0x30; //Segmento de datos de usuario- Protegemos con paginacion
	entradaTSS->ds = (unsigned short) 0x30; //Segmento de datos de usuario- Protegemos con paginacion
	entradaTSS->iomap = (unsigned short)0xFFFF;
	
	//Obtengo offset en la GDT para colocar un descriptor de TSS
	gdt_entry *tssDesc = obtener_sig_gdt_libre();
	tssDesc->limit_0_15 = (unsigned short)0x67;
	tssDesc->base_0_15 = (unsigned short) (unsigned int) entradaTSS;
	tssDesc->base_23_16 = (unsigned char) (unsigned int)entradaTSS>>16;
	tssDesc->base_31_24 = (unsigned char) (unsigned int)entradaTSS>>24;
	tssDesc->limit_16_19 = (unsigned char) 0;
	tssDesc->type = (unsigned char)0x9;
	tssDesc->s = (unsigned char)0;
	tssDesc->dpl = (unsigned char)0;
	tssDesc->p = (unsigned char)1;
	tssDesc->l = (unsigned char)0;
	tssDesc->g = (unsigned char)0;
	tssDesc->db = (unsigned char)0;
	tssDesc->avl = (unsigned char)1;	
	
	//Pido una entrada en la lista de tareas del scheduler
	unsigned short *entradaTarea = entradaSched();
	if(entradaTarea== (unsigned short*) -1){
		return  (unsigned short) -1;
	}
	else{
	*entradaTarea = (unsigned short) (tssDesc-gdt)*8;
	cantTaskActual++;
	}
	lcr3(cr3Tarea);
	return *entradaTarea;
	
}


unsigned short *entradaSched(){
	static unsigned short indiceTareas=0;
	static unsigned int entradasLibres= CANT_TAREAS;

	unsigned short *retorno = (unsigned short*)-1;
	if(entradasLibres--)
		retorno = &tareas[indiceTareas++];
	
	return retorno;
	
}

int obtener_pid(){
	unsigned short selTask = rtr();
	int i=0;
	if(tareas[0]==0){
	return -1;
	}else{
		
		while (tareas[i]!= selTask && i<CANT_TAREAS ){
		i++;
		}
	}
	return i;		
}

unsigned short proximo_indice(){
	int i =0;
	unsigned int tr;
	/*int indice = obtener_pid();
	if (indice != -1){
		if(indice == cantTaskActual-1){
		indice =0;
		}else{
		indice++;
		}
	} else {
		indice = 0;	//Esto es feo. ¡ARREGLAR!
	}
	//¡No puede ejecutarse esto si indice = -1
	breakpoint();
	unsigned short result = tareas[indice];
	*/
	tr = rtr();
	while(tr != tareas[i++]);
	
	if (i<CANT_TAREAS && tareas[i]!=0)
		return tareas[i];
	else 
		return tareas[0];	
	//return result;
}


unsigned int cantTareas() {
	return cantTaskActual;
}
